// 《围棋》作者版权所有。保留所有权利。
// 此源代码的使用受BSD样式
// 许可证的约束，该许可证可以在许可证文件中找到。

package fuzz

import (
	"bytes"
	"context"
	"crypto/sha256"
	"encoding/json"
	"errors"
	"fmt"
	"io"
	"io/ioutil"
	"os"
	"os/exec"
	"reflect"
	"runtime"
	"sync"
	"time"
)

const (
	// workerFuzzDuration是一名员工可以花费在测试协调员给出的输入的随机变化上的时间量。
	workerFuzzDuration = 100 * time.Millisecond

	// workerTimeoutDuration是一个工人在停止工作之前，不需要回复协调员的时间量。huang jian fuzz worker进程在出现内部错误后，将workerExitCode用作退出代码。
	workerTimeoutDuration = 1 * time.Second

	// 这将内部错误与失控的恐慌和其他崩溃区分开来。
	// 与内部/模糊保持同步。WorkerExit代码。
	workerExitCode = 70

	// workerSharedMemSize是用于与工作人员通信的共享内存文件的最大大小。这限制了模糊输入的大小。
	workerSharedMemSize = 100 << 20 // 100 MB 
)

// 工作进程管理运行测试二进制文件的工作进程。worker对象
// 只存在于协调器中（该过程由'go test-fuzz'启动）。
// 协调器使用workerClient将RPC发送到工作进程，
// 由workerServer处理这些RPC。
type worker struct {
	dir     string   // 工作目录，与包目录
	binPath string   // 测试可执行文件的路径
	args    []string // 测试可执行文件的参数
	env     []string // 测试可执行文件的环境

	coordinator *coordinator

	memMu chan *sharedMem // 与工作者共享内存的互斥保护；跨流程持续存在。

	cmd         *exec.Cmd     // 当前工作进程
	client      *workerClient // 用于与工作进程通信
	waitErr     error         // 等待返回的最后一个错误，在termC关闭之前设置。
	interrupted bool          // stop中断正在运行的工作人员后为true。
	termC       chan struct{} // 在工作进程终止时通过等待关闭
}

func newWorker(c *coordinator, dir, binPath string, args, env []string) (*worker, error) {
	mem, err := sharedMemTempFile(workerSharedMemSize)
	if err != nil {
		return nil, err
	}
	memMu := make(chan *sharedMem, 1)
	memMu <- mem
	return &worker{
		dir:         dir,
		binPath:     binPath,
		args:        args,
		env:         env[:len(env):len(env)], // 在追加时复制，以确保工作进程不会相互覆盖。
		coordinator: c,
		memMu:       memMu,
	}, nil
}

// cleanup释放与工作进程关联的持久资源。
func (w *worker) cleanup() error {
	mem := <-w.memMu
	if mem == nil {
		return nil
	}
	close(w.memMu)
	return mem.Close()
}

// 坐标运行测试二进制文件以执行模糊化。
// 
// 协调循环，直到ctx被取消或遇到致命错误。
// 如果测试进程在模糊化过程中意外终止，坐标将尝试重新启动并继续，除非终止可归因于中断（来自计时器或用户）。
// 
// 循环时，坐标从协调器接收输入，将这些输入传递给工作进程，然后将结果传回协调器。
func (w *worker) coordinate(ctx context.Context) error {
	// 主事件循环。
	for {
		// 如果工作进程未运行，请启动或重新启动它。
		if !w.isRunning() {
			if err := w.startAndPing(ctx); err != nil {
				return err
			}
		}

		select {
		case <-ctx.Done():
			// 工人被告知停止工作。
			err := w.stop()
			if err != nil && !w.interrupted && !isInterruptError(err) {
				return err
			}
			return ctx.Err()

		case <-w.termC:
			// 工作进程在等待输入时意外终止。
			err := w.stop()
			if w.interrupted {
				panic("worker interrupted after unexpected termination")
			}
			if err == nil || isInterruptError(err) {
				// 工作人员已停止，退出时状态为0，或退出后
				// 被协调员未发送的信号中断。
				// 
				// 当用户按^C时，在POSIX平台上，SIGINT被同时传递给
				// 组中的所有进程，工作人员可能会在协调员面前看到它
				// 。工人应该优雅地退出0（在
				// 理论中）。
				// 
				// 这种情况可能是用户故意造成的，所以请抑制
				// 错误。
				return nil
			}
			if exitErr, ok := err.(*exec.ExitError); ok && exitErr.ExitCode() == workerExitCode {
				// 工作进程退出，代码指示F.Fuzz调用不正确，
				// 例如，首先调用F.Fail。
				return fmt.Errorf("fuzzing process exited unexpectedly due to an internal failure: %w", err)
			}
			// 工作进程在模糊化时退出非零或被非中断
			// 信号（例如SIGSEGV）终止。
			return fmt.Errorf("fuzzing process hung or terminated unexpectedly: %w", err)
			// TODO（杰康罗德，凯蒂霍克曼）：如果-继续模糊，重新启动工人。

		case input := <-w.coordinator.inputC:
			// 收到协调员的输入。
			args := fuzzArgs{
				Limit:        input.limit,
				Timeout:      input.timeout,
				Warmup:       input.warmup,
				CoverageData: input.coverageData,
			}
			entry, resp, isInternalError, err := w.client.fuzz(ctx, input.entry, args)
			canMinimize := true
			if err != nil {
				// 与工作人员通信时出错。
				w.stop()
				if ctx.Err() != nil {
					// 超时或中断。
					return ctx.Err()
				}
				if w.interrupted {
					// 在我们停止工作之前，通信错误。
					// 报告错误，但不要记录崩溃者。
					return fmt.Errorf("communicating with fuzzing process: %v", err)
				}
				if sig, ok := terminationSignal(w.waitErr); ok && !isCrashSignal(sig) {
					// 工作进程被一个信号终止，该信号可能不是由
					// fuzz函数的特定输入引起的。例如，在Linux上，内核（OOM killer）可能会向使用大量内存的进程发送SIGKILL。或者，当终端
					// 关闭时，外壳可能会发出信号。不要记录撞车者。
					return fmt.Errorf("fuzzing process terminated by unexpected signal; no crash will be recorded: %v", w.waitErr)
				}
				if isInternalError {
					// 发生内部错误，不应将其视为崩溃。
					return err
				}
				// 意外终止。设置错误消息，并通过。
				// 我们将在下一次迭代中重新启动worker。
				// 不要试图将其最小化，因为它使工人崩溃。
				resp.Err = fmt.Sprintf("fuzzing process hung or terminated unexpectedly: %v", w.waitErr)
				canMinimize = false
			}
			result := fuzzResult{
				limit:         input.limit,
				count:         resp.Count,
				totalDuration: resp.TotalDuration,
				entryDuration: resp.InterestingDuration,
				entry:         entry,
				crasherMsg:    resp.Err,
				coverageData:  resp.CoverageData,
				canMinimize:   canMinimize,
			}
			w.coordinator.resultC <- result

		case input := <-w.coordinator.minimizeC:
			// 接收到来自协调器的输入以最小化。
			result, err := w.minimize(ctx, input)
			if err != nil {
				// 错误最小化。返回原始输入。如果之前没有导致
				// 错误，请立即报告为导致错误。
				// TODO:当
				// 执行-keepfuzzing时，请仔细检查是否正确处理了此问题。
				result = fuzzResult{
					entry:       input.entry,
					crasherMsg:  input.crasherMsg,
					canMinimize: false,
					limit:       input.limit,
				}
				if result.crasherMsg == "" {
					result.crasherMsg = err.Error()
				}
			}
			w.coordinator.resultC <- result
		}
	}
}

// minimize告诉工作进程尝试找到一个较小的值，该值导致错误（如果我们因为发现一个导致错误的输入
// 而开始最小化），或者保留新的覆盖范围（如果我们因为发现一个扩展覆盖范围的输入而开始最小化
// 则）。
func (w *worker) minimize(ctx context.Context, input fuzzMinimizeInput) (min fuzzResult, err error) {
	if w.coordinator.opts.MinimizeTimeout != 0 {
		var cancel func()
		ctx, cancel = context.WithTimeout(ctx, w.coordinator.opts.MinimizeTimeout)
		defer cancel()
	}

	args := minimizeArgs{
		Limit:        input.limit,
		Timeout:      input.timeout,
		KeepCoverage: input.keepCoverage,
	}
	entry, resp, err := w.client.minimize(ctx, input.entry, args)
	if err != nil {
		// 与工作人员通信时出错。
		w.stop()
		if ctx.Err() != nil || w.interrupted || isInterruptError(w.waitErr) {
			// 工作程序被中断，可能是用户按^C造成的。
			// 正常情况下，工作程序可以优雅地处理中断和超时，
			// 将返回，不会出错。此处的错误表示工作人员
			// 可能没有处于良好状态，但该错误对用户来说没有意义。只需返回原始破碎机，无需记录任何内容。
			return fuzzResult{
				entry:        input.entry,
				crasherMsg:   input.crasherMsg,
				coverageData: input.keepCoverage,
				canMinimize:  false,
				limit:        input.limit,
			}, nil
		}
		return fuzzResult{
			entry:         entry,
			crasherMsg:    fmt.Sprintf("fuzzing process hung or terminated unexpectedly while minimizing: %v", err),
			canMinimize:   false,
			limit:         input.limit,
			count:         resp.Count,
			totalDuration: resp.Duration,
		}, nil
	}

	if input.crasherMsg != "" && resp.Err == "" {
		return fuzzResult{}, fmt.Errorf("attempted to minimize a crash but could not reproduce")
	}

	return fuzzResult{
		entry:         entry,
		crasherMsg:    resp.Err,
		coverageData:  resp.CoverageData,
		canMinimize:   false,
		limit:         input.limit,
		count:         resp.Count,
		totalDuration: resp.Duration,
	}, nil
}

func (w *worker) isRunning() bool {
	return w.cmd != nil
}

// startAndPing启动工作进程并向其发送一条消息，以确保其
// 可以通信。
// 
// startAndPing如果此操作的任何部分不起作用，包括如果
// 上下文已过期或工作进程在其
// 响应之前被中断，则返回错误。启动后但ping响应之前发生的错误
// 可能表明工作人员没有先调用F.Fuzz或F.Fail。
// 我们不会记录这些错误的崩溃者。
func (w *worker) startAndPing(ctx context.Context) error {
	if ctx.Err() != nil {
		return ctx.Err()
	}
	if err := w.start(); err != nil {
		return err
	}
	if err := w.client.ping(ctx); err != nil {
		w.stop()
		if ctx.Err() != nil {
			return ctx.Err()
		}
		if isInterruptError(err) {
			// 在工作人员响应之前，用户可能按了^C。
			return err
		}
		// TODO:记录并返回stderr。
		return fmt.Errorf("fuzzing process terminated without fuzzing: %w", err)
	}
	return nil
}

// start运行一个新的工作进程。
// 
// 如果进程无法启动，start将返回一个错误。Start将不会
// 如果出现以后的终止错误，将从进程返回。
// 
// 如果进程启动成功，start返回nil。stop必须被称为
// 一次之后才能清理，即使进程自行终止。
// 
// 当进程终止时，w.waitErr设置为错误（如果有），
// w.termC关闭。
func (w *worker) start() (err error) {
	if w.isRunning() {
		panic("worker already started")
	}
	w.waitErr = nil
	w.interrupted = false
	w.termC = nil

	cmd := exec.Command(w.binPath, w.args...)
	cmd.Dir = w.dir
	cmd.Env = w.env[:len(w.env):len(w.env)] // 在append上复制以确保工作人员不会相互覆盖。

	// 创建“fuzz_in”和“fuzz_out”管道，以便我们可以与
	// 工人进行通信。我们不使用stdin和stdout，因为测试二进制文件可能会
	// 用它们做其他事情。
	// 
	// 每根管道都有一个读写器。协调员写信给fuzzInW 
	// 并从fuzzOutR读取。工人继承了fuzzInR和fuzzouth。
	// 协调员在启动工人后关闭fuzzInR和FuzzOut，因为我们不再需要它们。
	fuzzInR, fuzzInW, err := os.Pipe()
	if err != nil {
		return err
	}
	defer fuzzInR.Close()
	fuzzOutR, fuzzOutW, err := os.Pipe()
	if err != nil {
		fuzzInW.Close()
		return err
	}
	defer fuzzOutW.Close()
	setWorkerComm(cmd, workerComm{fuzzIn: fuzzInR, fuzzOut: fuzzOutW, memMu: w.memMu})

	// 启动辅助进程。
	if err := cmd.Start(); err != nil {
		fuzzInW.Close()
		fuzzOutR.Close()
		return err
	}

	// 工作程序已成功启动。
	// 在此之后，w.client拥有fuzzInW和fuzzOutR，所以w.client。Close必须是
	// 稍后由stop调用。
	w.cmd = cmd
	w.termC = make(chan struct{})
	comm := workerComm{fuzzIn: fuzzInW, fuzzOut: fuzzOutR, memMu: w.memMu}
	m := newMutator()
	w.client = newWorkerClient(comm, m)

	go func() {
		w.waitErr = w.cmd.Wait()
		close(w.termC)
	}()

	return nil
}

// stop通过关闭w.client通知工作进程退出，然后阻塞，直到
// 终止。如果工作人员在短时间内没有终止，停止
// 用操作系统发出信号。中断（如果支持），然后是操作系统。杀死
// 
// stop返回进程终止时出现的错误（与
// w.waitErr相同）。zhong zhong
func (w *worker) stop() error {
	if w.termC == nil {
		panic("worker was not started successfully")
	}
	select {
	case <-w.termC:
		// 工人已被解雇。
		if w.client == nil {
			// 已呼叫停止。
			return w.waitErr
		}
		// 可能的意外终止。
		w.client.Close()
		w.cmd = nil
		w.client = nil
		return w.waitErr
	default:
		// 工人仍在运行。
	}

	// 告诉工人关闭fuzz_以停止工作。直到
	// 完成之前的调用，它才真正停止。
	closeC := make(chan struct{})
	go func() {
		w.client.Close()
		close(closeC)
	}()

	sig := os.Interrupt
	if runtime.GOOS == "windows" {
		// 每https:
		// Windows；在操作系统中使用它。过程信号将返回一个错误。“
		// 退后杀戮。
		sig = os.Kill
	}

	t := time.NewTimer(workerTimeoutDuration)
	for {
		select {
		case <-w.termC:
			// 工人被解雇。
			t.Stop()
			<-closeC
			w.cmd = nil
			w.client = nil
			return w.waitErr

		case <-t.C:
			// 在工作人员终止前计时器被触发。
			w.interrupted = true
			switch sig {
			case os.Interrupt:
				// 试着用SIGINT阻止工人，再等一会儿。
				w.cmd.Process.Signal(sig)
				sig = os.Kill
				t.Reset(workerTimeoutDuration)

			case os.Kill:
				// 试着用SIGKILL阻止工人并继续等待。
				w.cmd.Process.Signal(sig)
				sig = nil
				t.Reset(workerTimeoutDuration)

			case nil:
				// 仍在等待。打印一条消息，让用户知道原因。
				fmt.Fprintf(w.coordinator.opts.Log, "waiting for fuzzing process to terminate...\n")
			}
		}
	}
}

// 在工作进程中调用RunFuzzWorker与
// 协调进程，以模糊随机输入。运行fuzzworker循环
// 直到协调器告诉它停止。
// 
// fn是fuzz函数的包装器。它可能返回一个错误，指示给定输入“崩溃”。如果
// 函数超时或终止进程，协调器也会记录崩溃。
// 
// RunFuzzWorker如果无法与
// 协调进程通信，则返回错误。
func RunFuzzWorker(ctx context.Context, fn func(CorpusEntry) error) error {
	comm, err := getWorkerComm()
	if err != nil {
		return err
	}
	srv := &workerServer{
		workerComm: comm,
		fuzzFn: func(e CorpusEntry) (time.Duration, error) {
			timer := time.AfterFunc(10*time.Second, func() {
				panic("deadlocked!") // 此错误消息不会被打印
			})
			defer timer.Stop()
			start := time.Now()
			err := fn(e)
			return time.Since(start), err
		},
		m: newMutator(),
	}
	return srv.serve(ctx)
}

// 调用已序列化，并从fuzz_中的协调器发送。它充当
// 一种极简RPC机制。必须将其中一个字段设置为指示
// 要调用的方法。
type call struct {
	Ping     *pingArgs
	Fuzz     *fuzzArgs
	Minimize *minimizeArgs
}

// minimizeArgs包含workerServer的参数。减少
// 最小化的值已在共享内存中。
type minimizeArgs struct {
	// 超时是用来最小化的时间。这可能包括启动时间
	// 尤其是当输入导致工作进程终止，需要
	// 重复重新启动时。
	Timeout time.Duration

	// Limit是要测试的最大值数，且不需要花费比持续时间更多的时间。0表示没有限制。
	Limit int64

	// KeepCoverage是一组覆盖率计数器，工作人员应尝试将其保持在最小值中。提供时，工作者将拒绝
	// 不会导致至少设置其中一个位的输入。
	KeepCoverage []byte

	// 索引是要最小化的模糊目标参数的索引。
	Index int
}

// 最小化响应包含workerServer的结果。减少
type minimizeResponse struct {
	// 如果工作人员发现较小的输入并将其写入
	// 共享内存，则WroteToMem为真。如果你有耳朵。设置了KeepCoverage，最小化输入
	// 至少保留了一个覆盖位，并且没有导致错误。
	// 否则，最小化的输入会导致一些错误，记录在Err中。
	WroteToMem bool

	// Err是由共享内存中的值（如果有）引起的错误字符串。
	Err string

	// coverage data是由共享内存中的最小值
	// 激活的覆盖位集。设置时，它至少包含KeepCoverage中的一位。
	// 如果设置了Err或最小化失败，则覆盖率数据将为零。
	CoverageData []byte

	// 持续时间是指最小化所花费的时间，不包括启动或清理。
	Duration time.Duration

	// Count是测试的值的数量。
	Count int64
}

// fuzzArgs包含workerServer的参数。绒毛。fuzz的值是
// 在共享内存中传递。
type fuzzArgs struct {
	// 超时是花在模糊化上的时间，不包括启动或
	// 清理。
	Timeout time.Duration

	// Limit是要测试的最大值数，且所花费的时间不超过持续时间。0表示没有限制。
	Limit int64

	// 预热指示这是否是预热运行的一部分，这意味着
	// 不应出现模糊。如果coverageEnabled为true，则应报告覆盖率数据
	// 。
	Warmup bool

	// 覆盖率数据是覆盖率数据。如果设置了，工作人员应该在模糊化之前更新其
	// 本地覆盖数据。
	CoverageData []byte
}

// fuzzResponse包含workerServer的结果。绒毛。
type fuzzResponse struct {
	// 持续时间是模糊处理所花费的时间，不包括启动或清理。
	TotalDuration       time.Duration
	InterestingDuration time.Duration

	// Count是测试的值的数量。
	Count int64

	// 如果共享内存中的值扩展了覆盖范围，则会设置coverage data 
	// 因此协调器可能会感兴趣。
	CoverageData []byte

	// Err是共享内存中的值导致的错误字符串，如果共享内存中的值导致崩溃，则为
	// 非空。
	Err string

	// InternalErr是由
	// worker中的内部错误导致的错误字符串。这不应该被认为是一个不速之客。
	InternalErr string
}

// pingArgs包含workerServer的参数。发出砰的声响。
type pingArgs struct{}

// pingResponse包含workerServer的结果。发出砰的声响。
type pingResponse struct{}

// WorkerCommand保存用于协调进程（客户端）和工作进程（服务器）之间通信的管道和共享内存。
// 这些值对每个工人都是唯一的；它们只与
// 协调员共享，不与其他工人共享。
// 
// 通过RPC协议对共享内存的访问进行隐式同步
// 在workerServer和workerClient中实现。在通话过程中，客户端
// /（工作者）以独占方式访问共享内存；在其他时候，服务器
// （协调人）拥有独占访问权限。
type workerComm struct {
	fuzzIn, fuzzOut *os.File
	memMu           chan *sharedMem // 互斥保护共享内存
}

// workerServer是一个最低限度的RPC服务器，由模糊工作进程运行。
// 它允许协调进程（使用workerClient）调用
// 工作进程中的方法。此系统允许协调器并行运行多个工作进程
// 进程，并在工作进程意外终止后从共享
// 内存中收集导致崩溃的输入。
type workerServer struct {
	workerComm
	m *mutator

	// coverage任务是员工的本地覆盖率数据。它是
	// 当发现新的
	// 覆盖范围时，会定期更新以反映协调器中的数据。
	coverageMask []byte

	// fuzzFn在给定的输入上运行worker的fuzz目标，如果发现崩溃程序（进程也可能退出或崩溃），则返回一个
	// 错误，以及运行输入所用的时间。它设定了一个10秒的截止时间，在这个时间点上，
	// 它会惊慌失措，因为它假设进程挂起或
	// 陷入僵局。
	fuzzFn func(CorpusEntry) (time.Duration, error)
}

// serve读取fuzzIn上的序列化RPC消息。当serve收到消息时，
// 它调用相应的方法，然后在fuzzOut上将序列化结果发回
// 。
// 
// serve同步处理RPC调用；在上一次通话结束之前，它不会尝试读取消息
// 。
// 
// serve返回通过管道进行通信时发生的错误。serve 
// 不返回方法调用的错误；这些都是通过序列化的
// 响应传递的。
func (ws *workerServer) serve(ctx context.Context) error {
	enc := json.NewEncoder(ws.fuzzOut)
	dec := json.NewDecoder(&contextReader{ctx: ctx, r: ws.fuzzIn})
	for {
		var c call
		if err := dec.Decode(&c); err != nil {
			if err == io.EOF || err == ctx.Err() {
				return nil
			} else {
				return err
			}
		}

		var resp any
		switch {
		case c.Fuzz != nil:
			resp = ws.fuzz(ctx, *c.Fuzz)
		case c.Minimize != nil:
			resp = ws.minimize(ctx, *c.Minimize)
		case c.Ping != nil:
			resp = ws.ping(ctx, *c.Ping)
		default:
			return errors.New("no arguments provided for any call")
		}

		if err := enc.Encode(resp); err != nil {
			return err
		}
	}
}

// chainedMutations是在工作者
// 将输入重置为原始状态之前应用的突变数。
// 注意：这个数字是不假思索选择的。它足够低，以至于
// 它似乎在突变输入中创造了显著的多样性。我们可能希望ABCDEG
// 突变数。fuzz方法（在
// 看来，这是libFuzzer所做的，尽管似乎没有任何文档可以解释为什么做出这个选择。）
const chainedMutations = 5

// fuzz在有限的持续时间或迭代次数内，对共享
// 内存中输入值的随机变化运行测试函数。
// 
// fuzz如果发现一个导致fuzz函数崩溃的输入（使用
// fuzzResponse.Err集）或一个扩展覆盖范围的输入（使用
// fuzzResponse.InterestingDuration集），就会提前返回。
// 
// fuzz不会修改共享内存中的输入。相反，它将
// 初始PRNG状态保存在共享内存中，并在每次调用测试函数之前增加共享
// 内存中的一个计数器。由于PRNG是确定性的，因此调用者可以使用此信息重构
// 崩溃输入。
func (ws *workerServer) fuzz(ctx context.Context, args fuzzArgs) (resp fuzzResponse) {
	if args.CoverageData != nil {
		if ws.coverageMask != nil && len(args.CoverageData) != len(ws.coverageMask) {
			resp.InternalErr = fmt.Sprintf("unexpected size for CoverageData: got %d, expected %d", len(args.CoverageData), len(ws.coverageMask))
			return resp
		}
		ws.coverageMask = args.CoverageData
	}
	start := time.Now()
	defer func() { resp.TotalDuration = time.Since(start) }()

	if args.Timeout != 0 {
		var cancel func()
		ctx, cancel = context.WithTimeout(ctx, args.Timeout)
		defer cancel()
	}
	mem := <-ws.memMu
	ws.m.r.save(&mem.header().randState, &mem.header().randInc)
	defer func() {
		resp.Count = mem.header().count
		ws.memMu <- mem
	}()
	if args.Limit > 0 && mem.header().count >= args.Limit {
		resp.InternalErr = fmt.Sprintf("mem.header().count %d already exceeds args.Limit %d", mem.header().count, args.Limit)
		return resp
	}

	originalVals, err := unmarshalCorpusFile(mem.valueCopy())
	if err != nil {
		resp.InternalErr = err.Error()
		return resp
	}
	vals := make([]any, len(originalVals))
	copy(vals, originalVals)

	shouldStop := func() bool {
		return args.Limit > 0 && mem.header().count >= args.Limit
	}
	fuzzOnce := func(entry CorpusEntry) (dur time.Duration, cov []byte, errMsg string) {
		mem.header().count++
		var err error
		dur, err = ws.fuzzFn(entry)
		if err != nil {
			errMsg = err.Error()
			if errMsg == "" {
				errMsg = "fuzz function failed with no input"
			}
			return dur, nil, errMsg
		}
		if ws.coverageMask != nil && countNewCoverageBits(ws.coverageMask, coverageSnapshot) > 0 {
			return dur, coverageSnapshot, ""
		}
		return dur, nil, ""
	}

	if args.Warmup {
		dur, _, errMsg := fuzzOnce(CorpusEntry{Values: vals})
		if errMsg != "" {
			resp.Err = errMsg
			return resp
		}
		resp.InterestingDuration = dur
		if coverageEnabled {
			resp.CoverageData = coverageSnapshot
		}
		return resp
	}

	for {
		select {
		case <-ctx.Done():
			return resp
		default:
			if mem.header().count%chainedMutations == 0 {
				copy(vals, originalVals)
				ws.m.r.save(&mem.header().randState, &mem.header().randInc)
			}
			ws.m.mutate(vals, cap(mem.valueRef()))

			entry := CorpusEntry{Values: vals}
			dur, cov, errMsg := fuzzOnce(entry)
			if errMsg != "" {
				resp.Err = errMsg
				return resp
			}
			if cov != nil {
				resp.CoverageData = cov
				resp.InterestingDuration = dur
				return resp
			}
			if shouldStop() {
				return resp
			}
		}
	}
}

func (ws *workerServer) minimize(ctx context.Context, args minimizeArgs) (resp minimizeResponse) {
	start := time.Now()
	defer func() { resp.Duration = time.Now().Sub(start) }()
	mem := <-ws.memMu
	defer func() { ws.memMu <- mem }()
	vals, err := unmarshalCorpusFile(mem.valueCopy())
	if err != nil {
		panic(err)
	}
	inpHash := sha256.Sum256(mem.valueCopy())
	if args.Timeout != 0 {
		var cancel func()
		ctx, cancel = context.WithTimeout(ctx, args.Timeout)
		defer cancel()
	}

	// 最小化VAL中的值，然后写入共享内存。我们只在完成最小化后将
	// 写入共享内存。
	success, err := ws.minimizeInput(ctx, vals, mem, args)
	if success {
		writeToMem(vals, mem)
		outHash := sha256.Sum256(mem.valueCopy())
		mem.header().rawInMem = false
		resp.WroteToMem = true
		if err != nil {
			resp.Err = err.Error()
		} else {
			// 如果值在最小化过程中没有改变，那么coverageSnapshot很可能是一个脏快照，它代表了最小化的最后一步，而不是初始输入的覆盖率。在这种情况下，只需返回我们最初给出的
			// 覆盖范围，因为它更准确地表示我们返回的
			// 输入的覆盖范围图。
			if outHash != inpHash {
				resp.CoverageData = coverageSnapshot
			} else {
				resp.CoverageData = args.KeepCoverage
			}
		}
	}
	return resp
}

// minimizeInput在提供的
// VAL上应用一系列最小化转换，确保每次最小化仍会导致错误，或保持
// coverage处于模糊状态fn。它使用上下文来确定运行多长时间，
// 关闭后停止。它返回一个bool，指示最小化是否成功，如果找到则返回一个错误。
func (ws *workerServer) minimizeInput(ctx context.Context, vals []any, mem *sharedMem, args minimizeArgs) (success bool, retErr error) {
	keepCoverage := args.KeepCoverage
	memBytes := mem.valueRef()
	bPtr := &memBytes
	count := &mem.header().count
	shouldStop := func() bool {
		return ctx.Err() != nil ||
			(args.Limit > 0 && *count >= args.Limit)
	}
	if shouldStop() {
		return false, nil
	}

	// 检查原始值是否保留覆盖范围或导致错误。
	// 如果不是，那么无论是什么让我们觉得这个值有趣，都可能是一片雪花，我们无法将其最小化。
	*count++
	_, retErr = ws.fuzzFn(CorpusEntry{Values: vals})
	if keepCoverage != nil {
		if !hasCoverageBit(keepCoverage, coverageSnapshot) || retErr != nil {
			return false, nil
		}
	} else if retErr == nil {
		return false, nil
	}
	mem.header().rawInMem = true

	// trymminimized运行fuzz函数，用候选值替换索引valI处的值
	// 。tryMinimized返回带有候选项的输入是否是
	// 有趣，原因与原始输入相同：它返回
	// 如果预期错误，则返回错误，或者保留覆盖范围。
	tryMinimized := func(candidate []byte) bool {
		prev := vals[args.Index]
		switch prev.(type) {
		case []byte:
			vals[args.Index] = candidate
		case string:
			vals[args.Index] = string(candidate)
		default:
			panic("impossible")
		}
		copy(*bPtr, candidate)
		*bPtr = (*bPtr)[:len(candidate)]
		mem.setValueLen(len(candidate))
		*count++
		_, err := ws.fuzzFn(CorpusEntry{Values: vals})
		if err != nil {
			retErr = err
			if keepCoverage != nil {
				// 现在我们发现了一个崩溃，这比任何
				// 最小化正在进行的有趣输入都更重要。清除
				// keepCoverage只会将未来的崩溃降至最低。
				keepCoverage = nil
			}
			return true
		}
		// 最小化应该保留覆盖位。
		if keepCoverage != nil && isCoverageSubset(keepCoverage, coverageSnapshot) {
			return true
		}
		vals[args.Index] = prev
		return false
	}
	switch v := vals[args.Index].(type) {
	case string:
		minimizeBytes([]byte(v), tryMinimized, shouldStop)
	case []byte:
		minimizeBytes(v, tryMinimized, shouldStop)
	default:
		panic("impossible")
	}
	return true, retErr
}

func writeToMem(vals []any, mem *sharedMem) {
	b := marshalCorpusFile(vals...)
	mem.setValue(b)
}

// ping什么也不做。协调器调用此方法以确保工作人员
// 已调用F.Fuzz并可以进行通信。
func (ws *workerServer) ping(ctx context.Context, args pingArgs) pingResponse {
	return pingResponse{}
}

// workerClient是一个极简主义的RPC客户端。协调进程使用
// workerClient调用每个工作进程中的方法（由
// workerServer处理）。
type workerClient struct {
	workerComm
	m *mutator

	// mu是保护workerComm的互斥锁。管子里有绒毛。在调用workerServer之前，这必须是
	// 锁定的。它阻止
	// workerClient。Close from closing fuzzIn，而workerClient方法是
	// 同时向其写入，并防止多个调用方同时向
	// fuzzIn写入。
	mu sync.Mutex
}

func newWorkerClient(comm workerComm, m *mutator) *workerClient {
	return &workerClient{workerComm: comm, m: m}
}

// 关闭通过
// 关闭fuzz_in关闭到RPC服务器（工作进程）的连接。Close将fuzz_排出（避免工作进程中出现SIGPIPE），
// 并在工作进程关闭另一端后将其关闭。
func (wc *workerClient) Close() error {
	wc.mu.Lock()
	defer wc.mu.Unlock()

	// 关闭fuzzIn。这会向服务器发出信号，表示不再有呼叫，
	// 并且应该退出。
	if err := wc.fuzzIn.Close(); err != nil {
		wc.fuzzOut.Close()
		return err
	}

	// 排空并关闭。当服务器退出时，内核将关闭
	// 它的fuzzOut结束，我们将获得EOF。
	if _, err := io.Copy(ioutil.Discard, wc.fuzzOut); err != nil {
		wc.fuzzOut.Close()
		return err
	}
	return wc.fuzzOut.Close()
}

// errSharedMemClosed由无法访问
// 共享内存的workerClient方法返回，因为它已被另一个goroutine关闭和取消映射。
// 在工作时可能发生。在worker goroutine中调用cleanup，而
// workerClient。fuzz调用同时运行。
// 
// 不应报告此错误。表示操作被
// 中断。
var errSharedMemClosed = errors.New("internal error: shared memory was closed and unmapped")

// minimize告诉工作者调用minimize方法。参见
// workerServer。减少
func (wc *workerClient) minimize(ctx context.Context, entryIn CorpusEntry, args minimizeArgs) (entryOut CorpusEntry, resp minimizeResponse, retErr error) {
	wc.mu.Lock()
	defer wc.mu.Unlock()

	mem, ok := <-wc.memMu
	if !ok {
		return CorpusEntry{}, minimizeResponse{}, errSharedMemClosed
	}
	mem.header().count = 0
	inp, err := corpusEntryData(entryIn)
	if err != nil {
		return CorpusEntry{}, minimizeResponse{}, err
	}
	mem.setValue(inp)
	defer func() { wc.memMu <- mem }()
	entryOut = entryIn
	entryOut.Values, err = unmarshalCorpusFile(inp)
	if err != nil {
		return CorpusEntry{}, minimizeResponse{}, fmt.Errorf("workerClient.minimize unmarshaling provided value: %v", err)
	}
	for i, v := range entryOut.Values {
		if !isMinimizable(reflect.TypeOf(v)) {
			continue
		}

		wc.memMu <- mem
		args.Index = i
		c := call{Minimize: &args}
		callErr := wc.callLocked(ctx, c, &resp)
		mem, ok = <-wc.memMu
		if !ok {
			return CorpusEntry{}, minimizeResponse{}, errSharedMemClosed
		}

		if callErr != nil {
			retErr = callErr
			if !mem.header().rawInMem {
				// 在最小化开始之前发生了一个不可恢复的错误。
				return entryIn, minimizeResponse{}, retErr
			}
			// 最小化过程中出现不可恢复的错误。mem现在
			// 保存entryIn的原始未经加密的字节。
			// 导致错误的值[i]。
			switch entryOut.Values[i].(type) {
			case string:
				entryOut.Values[i] = string(mem.valueCopy())
			case []byte:
				entryOut.Values[i] = mem.valueCopy()
			default:
				panic("impossible")
			}
			entryOut.Data = marshalCorpusFile(entryOut.Values...)
			// 停止最小化；可能会发生另一个无法恢复的错误。
			break
		}

		if resp.WroteToMem {
			// 最小化成功，mem保存封送的数据。
			entryOut.Data = mem.valueCopy()
			entryOut.Values, err = unmarshalCorpusFile(entryOut.Data)
			if err != nil {
				return CorpusEntry{}, minimizeResponse{}, fmt.Errorf("workerClient.minimize unmarshaling minimized value: %v", err)
			}
		}

		// 为循环的下一次迭代做准备。
		if args.Timeout != 0 {
			args.Timeout -= resp.Duration
			if args.Timeout <= 0 {
				break
			}
		}
		if args.Limit != 0 {
			args.Limit -= mem.header().count
			if args.Limit <= 0 {
				break
			}
		}
	}
	resp.Count = mem.header().count
	h := sha256.Sum256(entryOut.Data)
	entryOut.Path = fmt.Sprintf("%x", h[:4])
	return entryOut, resp, retErr
}

// fuzz告诉工作者调用fuzz方法。请参阅workerServer。绒毛。
func (wc *workerClient) fuzz(ctx context.Context, entryIn CorpusEntry, args fuzzArgs) (entryOut CorpusEntry, resp fuzzResponse, isInternalError bool, err error) {
	wc.mu.Lock()
	defer wc.mu.Unlock()

	mem, ok := <-wc.memMu
	if !ok {
		return CorpusEntry{}, fuzzResponse{}, true, errSharedMemClosed
	}
	mem.header().count = 0
	inp, err := corpusEntryData(entryIn)
	if err != nil {
		return CorpusEntry{}, fuzzResponse{}, true, err
	}
	mem.setValue(inp)
	wc.memMu <- mem

	c := call{Fuzz: &args}
	callErr := wc.callLocked(ctx, c, &resp)
	if resp.InternalErr != "" {
		return CorpusEntry{}, fuzzResponse{}, true, errors.New(resp.InternalErr)
	}
	mem, ok = <-wc.memMu
	if !ok {
		return CorpusEntry{}, fuzzResponse{}, true, errSharedMemClosed
	}
	defer func() { wc.memMu <- mem }()
	resp.Count = mem.header().count

	if !bytes.Equal(inp, mem.valueRef()) {
		return CorpusEntry{}, fuzzResponse{}, true, errors.New("workerServer.fuzz modified input")
	}
	needEntryOut := callErr != nil || resp.Err != "" ||
		(!args.Warmup && resp.CoverageData != nil)
	if needEntryOut {
		valuesOut, err := unmarshalCorpusFile(inp)
		if err != nil {
			return CorpusEntry{}, fuzzResponse{}, true, fmt.Errorf("unmarshaling fuzz input value after call: %v", err)
		}
		wc.m.r.restore(mem.header().randState, mem.header().randInc)
		if !args.Warmup {
			// 仅在实际发生模糊化时才对值sout进行变异。
			numMutations := ((resp.Count - 1) % chainedMutations) + 1
			for i := int64(0); i < numMutations; i++ {
				wc.m.mutate(valuesOut, cap(mem.valueRef()))
			}
		}
		dataOut := marshalCorpusFile(valuesOut...)

		h := sha256.Sum256(dataOut)
		name := fmt.Sprintf("%x", h[:4])
		entryOut = CorpusEntry{
			Parent:     entryIn.Path,
			Path:       name,
			Data:       dataOut,
			Generation: entryIn.Generation + 1,
		}
		if args.Warmup {
			// 字节没有变异，所以如果entryIn是一个种子语料库值，那么entryOut也是。
			entryOut.IsSeed = entryIn.IsSeed
		}
	}

	return entryOut, resp, false, callErr
}

// ping告诉工作者调用ping方法。请参阅workerServer。发出砰的声响。
func (wc *workerClient) ping(ctx context.Context) error {
	wc.mu.Lock()
	defer wc.mu.Unlock()
	c := call{Ping: &pingArgs{}}
	var resp pingResponse
	return wc.callLocked(ctx, c, &resp)
}

// callLocked从协调器向工作进程发送一个RPC，并等待
// 的响应。呼叫锁定可能会被ctx取消。
func (wc *workerClient) callLocked(ctx context.Context, c call, resp any) (err error) {
	enc := json.NewEncoder(wc.fuzzIn)
	dec := json.NewDecoder(&contextReader{ctx: ctx, r: wc.fuzzOut})
	if err := enc.Encode(c); err != nil {
		return err
	}
	return dec.Decode(resp)
}

// contextReader用上下文包装读卡器。如果上下文被取消
// 而基础读卡器被阻止，则Read立即返回。
// 
// 这对于从管道中读取数据非常有用。关闭管道文件描述符不会解除对该文件描述符的挂起读取的阻止。管道的
// 其他文件描述符（写入端）的所有副本必须在
// 继承它的所有进程中关闭。在我们关心
// /（过程组终止）的情况下，这很难正确执行。
type contextReader struct {
	ctx context.Context
	r   io.Reader
}

func (cr *contextReader) Read(b []byte) (int, error) {
	if ctxErr := cr.ctx.Err(); ctxErr != nil {
		return 0, ctxErr
	}
	done := make(chan struct{})

	// 由于基础
	// Read被阻止，因此在Read返回后，此goroutine可能会被阻止。
	var n int
	var err error
	go func() {
		n, err = cr.r.Read(b)
		close(done)
	}()

	select {
	case <-cr.ctx.Done():
		return 0, cr.ctx.Err()
	case <-done:
		return n, err
	}
}
